package org.example.oauth2;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Sa-OAuth2 Server端 控制器
 * @author click33
 * 
 */
@RestController
public class SaOAuth2ServerController {

	private static Logger log = LoggerFactory.getLogger(SaOAuth2ServerController.class);


	@RequestMapping("/oauth2/authorize")
	public Object authorize() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		Object obj = SaOAuth2Handle.serverRequest();
		log.info("-------" + SaHolder.getRequest().getUrl() + "-------响应: " + obj);
		return obj;
	}

	@RequestMapping("/oauth2/token")
	public Object token() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		SaRequest req = SaHolder.getRequest();
		List<String> list = req.getParamNames();
		Map<String, String> map1 = req.getParamMap();

		log.info("------- list: " + list);
		log.info("------- map: " + map1);

		String grantType = req.getParamNotNull(SaOAuth2Consts.Param.grant_type);
		if ("authorization_code".equals(grantType)) {
			String code = req.getParamNotNull(SaOAuth2Consts.Param.code);
			String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
			String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
			String redirectUri = req.getParam(SaOAuth2Consts.Param.redirect_uri);
			SaOAuth2Util.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
			AccessTokenModel token = SaOAuth2Util.generateAccessToken(code);
			Map<String, Object> tokenMap = token.toLineMap();
			HashMap map = new HashMap();
			map.put("access_token", tokenMap.get("access_token"));
			map.put("refresh_token", tokenMap.get("refresh_token"));
			map.put("expires_in", tokenMap.get("expires_in"));
			map.put("token_type", "bearer");
			log.info("-------" + SaHolder.getRequest().getUrl() + "-------响应: " + map);
			SaManager.getSaTokenDao().set(tokenMap.get("access_token").toString(), tokenMap.get("refresh_token").toString(), 10000L);
			return map;
		} else {
			String refreshToken = req.getParamNotNull(SaOAuth2Consts.Param.refresh_token);
			String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
			String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
			AccessTokenModel token = SaOAuth2Util.refreshAccessToken(refreshToken);
			Map<String, Object> tokenMap = token.toLineMap();
			HashMap map = new HashMap();
			map.put("access_token", tokenMap.get("access_token"));
			map.put("refresh_token", tokenMap.get("refresh_token"));
			map.put("expires_in", tokenMap.get("expires_in"));
			map.put("token_type", "bearer");
			log.info("-------" + SaHolder.getRequest().getUrl() + "-------响应: " + map);
			SaManager.getSaTokenDao().set(tokenMap.get("access_token").toString(), tokenMap.get("refresh_token").toString(), 10000L);
			return map;
		}
	}

	@RequestMapping("/oauth2/refresh")
	public Object refresh() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.serverRequest();
	}

	@RequestMapping("/oauth2/revoke")
	public Object revoke() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.serverRequest();
	}

	@RequestMapping("/oauth2/client_token")
	public Object clientToken() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		Object obj = SaOAuth2Handle.serverRequest();
		log.info("-------" + SaHolder.getRequest().getUrl() + "-------响应: " + obj);
		return obj;
	}

	@RequestMapping("/oauth2/doLogin")
	public Object doLogin() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.serverRequest();
	}

	@RequestMapping("/oauth2/doConfirm")
	public Object doConfirm() {
		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.serverRequest();
	}

	// Sa-OAuth2 定制化配置 
	@Autowired
	public void setSaOAuth2Config(SaOAuth2Config cfg) {
		cfg.
			// 未登录的视图 
			setNotLoginView(()-> new ModelAndView("login.html")).
			// 登录处理函数 
			setDoLoginHandle((name, pwd) -> {
				if(("sa".equals(name) ||  "sa1".equals(name) || "sa2".equals(name)) && "123456".equals(pwd)) {
					StpUtil.login(10001);
					return SaResult.ok();
				}
				return SaResult.error("账号名或密码错误");
			}).
			// 授权确认视图 
			setConfirmView((clientId, scope)->{
				Map<String, Object> map = new HashMap<>();
				map.put("clientId", clientId);
				map.put("scope", scope);
				return new ModelAndView("confirm.html", map); 
			})
			;
	}

	// 全局异常拦截  
	@ExceptionHandler
	public SaResult handlerException(Exception e) {
		e.printStackTrace(); 
		return SaResult.error(e.getMessage());
	}
	
	
	// ---------- 开放相关资源接口： Client端根据 Access-Token ，置换相关资源 ------------ 
	
	// 获取Userinfo信息：昵称、头像、性别等等
	@RequestMapping("/oauth2/userinfo")
	public SaResult userinfo() {
		// 获取 Access-Token 对应的账号id 
		String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
		Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
		System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
		
		// 校验 Access-Token 是否具有权限: userinfo
		SaOAuth2Util.checkScope(accessToken, "userinfo");
		
		// 模拟账号信息 （真实环境需要查询数据库获取信息）
		Map<String, Object> map = new LinkedHashMap<>();
		map.put("nickname", "Smile");
		map.put("avatar", "http://test/1.jpg");
		map.put("age", "18");
		map.put("sex", "男");
		map.put("address", "浙江省 杭州市");
		return SaResult.data(map);
	}
	
}
